﻿using AutoMapper;
using crip.Common;
using crip.Dtos;
using crip.Helper;
using crip.Models;
using crip.ResourceParameters;
using crip.Services.IRepositorys;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using System.Security.Claims;

namespace crip.Controllers
{
    /// <summary>
    /// 区域
    /// </summary>
    [Route("api/[controller]")]
    [ApiController]
    public class AreaController : ControllerBase
    {
        #region 依赖注入
        //用户工具包
        private readonly UserManager<CripUser> _userManager;
        //登录工具包
        private readonly SignInManager<CripUser> _signInManager;
        //角色工具包
        private readonly RoleManager<IdentityRole> _roleManager;
        //获取或设置配置值的IConfiguration接口
        private readonly IConfiguration _configuration;
        //缓存验证码
        private readonly IMemoryCache _cache;
        //使用HttpContext的接口
        private readonly IHttpContextAccessor _httpContextAccessor;
        //区域仓库
        private readonly IAreaRepository _areaRepository;
        //事件仓库
        private readonly IEventRepository _eventRepository;
        //链接生成工具
        private readonly IUrlHelper _urlHelper;
        //AutoMapper映射工具包
        private readonly IMapper _mapper;
        public AreaController(
            IConfiguration configuration,
            UserManager<CripUser> userManager,
            SignInManager<CripUser> signInManager,
            RoleManager<IdentityRole> roleManager,
            IMemoryCache cache,
            IHttpContextAccessor httpContextAccessor,
            IAreaRepository areaRepository,
            IEventRepository eventRepository,
            IActionContextAccessor actionContextAccessor,//定义用于公开操作上下文的接口。
            IUrlHelperFactory urlHelperFactory,
            IMapper mapper
            )
        {
            _configuration = configuration;
            _userManager = userManager;
            _signInManager = signInManager;
            _roleManager = roleManager;
            _cache = cache;
            _httpContextAccessor = httpContextAccessor;
            _areaRepository = areaRepository;
            _eventRepository = eventRepository;
            //获取 IUrlHelper 与 关联的请求的 context 。
            _urlHelper = urlHelperFactory.GetUrlHelper(actionContextAccessor.ActionContext);//作为 HTTP 请求的一部分选择的执行操作的上下文对象
            _mapper = mapper;
        }
        #endregion
        //私有方法
        #region 生成GetURL
        private string GenerateCripResourceURL(
          AreaParameter areaParameter,
          PaginationResourceParamaters paginationResourceParamaters,
          ResourceURLType type
          )
        {
            return type switch
            {
                ResourceURLType.PreviousPage => _urlHelper.Link("GetAreas",
                new
                {
                    keyword = areaParameter.Keyword,
                    orderby = areaParameter.OrderBy,
                    pageNumber = paginationResourceParamaters.PageNumber - 1,
                    pageSize = paginationResourceParamaters.PageSize
                }),
                ResourceURLType.NextPage => _urlHelper.Link("GetAreas",
                new
                {
                    keyword = areaParameter.Keyword,
                    orderby = areaParameter.OrderBy,
                    pageNumber = paginationResourceParamaters.PageNumber + 1,
                    pageSize = paginationResourceParamaters.PageSize
                }),
                _ => _urlHelper.Link("GetAreas",
                new
                {
                    keyword = areaParameter.Keyword,
                    orderby = areaParameter.OrderBy,
                    pageNumber = paginationResourceParamaters.PageNumber,
                    pageSize = paginationResourceParamaters.PageSize
                }
                )
            };
        }
        #endregion

        #region 多个资源CreateLinksForAreaList
        private IEnumerable<LinkDto> CreateLinksForAreaList(
          AreaParameter areaParameter,
          PaginationResourceParamaters paginationResourceParamaters
         )
        {
            var links = new List<LinkDto>();
            // 添加self，自我链接
            links.Add(new LinkDto(
                   GenerateCripResourceURL(
                        areaParameter, paginationResourceParamaters, ResourceURLType.CurrnetPage),
                    "self",
                    "GET"
                ));

            // "api/touristRoutes"
            // 添加创建旅游路线
            links.Add(new LinkDto(
                    Url.Link("CreateArea", null),
                    "create_area",
                    "POST"
                ));

            return links;
        }
        #endregion

        #region 单一资源CreateLinkForArea
        private LinkDto CreateLinkForArea(
    Guid areaId)
        {

            return new LinkDto(
                    Url.Link("GetAreaById", new { areaId }),
                    "self",
                    "GET"
                    );
        }
        #endregion

        #region AreaLinksForAreaId
        private List<LinkDto> AreaLinksForAreaId(
       IEnumerable<Guid> IDs)
        {
            List<LinkDto> res = new List<LinkDto>();
            IDs.ToList().ForEach(
                    id => {
                        res.Add(CreateLinkForArea(id));
                    }
                    );

            return res;
        }
        #endregion  

        //API
        #region 获取分页与Hateoas 的Area数据
        /// <summary>
        /// 获取分页与Hateoas 的Area数据
        /// </summary>
        /// <param name="areaParameter">keyword 模糊查找关键字，orderBy 根据什么排序，desc是否逆序 </param>
        /// <param name="paginationResourceParamaters">当前第几页，当前页数据量</param>
        /// <param name="mediaType">请求方式</param>
        /// <returns></returns>

        #region 设置允许请求的媒体类型
        [Produces(
            "application/json",
            "application/vnd.crip.hateoas+json",
            "application/vnd.crip.Area.simplify+json",
            "application/vnd.crip.Area.simplify.hateoas+json"
            )]
        #endregion

        [HttpGet(Name = "GetAreas")]
        public async Task<IActionResult> GetAreasAsync(
            [FromQuery] AreaParameter areaParameter,
            [FromQuery] PaginationResourceParamaters paginationResourceParamaters,
            [FromHeader(Name = "Accept")] string mediaType
            )
        {
            PagesResponse pagesResponse = new PagesResponse();
            IEnumerable<AreaSimplifyDto> areasSimplifyDto = new List<AreaSimplifyDto>();
            IEnumerable<AreaDto> areasDto = new List<AreaDto>();
            bool isHateoas = false;
            StringSegment primaryMediaType="";
            #region 获取资源与校验
            //获取媒体类型
            if (!MediaTypeHeaderValue
               .TryParse(mediaType, out MediaTypeHeaderValue parsedMediatype))
            {
                pagesResponse.BadRequest("未查找到媒体类型");
            }
            //从仓库获得分页资源
            var areaRepo = await _areaRepository
            .GetAllAreaAsync(
                areaParameter.Keyword,
                areaParameter.OrderBy,
                areaParameter.Desc,
                paginationResourceParamaters.PageNumber,
                paginationResourceParamaters.PageSize
                );
            var IDs = areaRepo.Select(x => x.Id);

            if (areaRepo == null || areaRepo.Count() == 0)
            {
                pagesResponse.NotFound("未找到区域");
                return NotFound(pagesResponse);
            }
            #endregion

            #region 分页URL
            //上一页的url
            var previousPageLink = areaRepo.HasPrevious
          ? GenerateCripResourceURL(
             areaParameter, paginationResourceParamaters, ResourceURLType.PreviousPage)
          : null;
            //下一页的url
            var nextPageLink = areaRepo.HasNext
          ? GenerateCripResourceURL(
             areaParameter, paginationResourceParamaters, ResourceURLType.NextPage)
          : null;
            #endregion

            #region 设置URL

            // x-pagination水平分页
            var paginationMetadata = new
            {
                previousPageLink,
                nextPageLink,
                totalCount = areaRepo.TotalCount,
                pageSize = areaRepo.PageSize,
                currentPage = areaRepo.CurrentPage,
                totalPages = areaRepo.TotalPages
            };
            HttpResponseHelper httpResponseHelper = new HttpResponseHelper();


            //分页链接设置到响应头
            httpResponseHelper.SetHeader("x-pagination", Newtonsoft.Json.JsonConvert.SerializeObject(paginationMetadata), Response);//信息设置到header
            #endregion


            if (parsedMediatype != null)
            {


                #region 媒体资源类型获取
                //截取是否hateoas请求
                isHateoas = parsedMediatype.SubTypeWithoutSuffix
                    .EndsWith("hateoas", StringComparison.InvariantCultureIgnoreCase);
                //截取主要请求的媒体类型
                primaryMediaType = isHateoas
                    ? parsedMediatype.SubTypeWithoutSuffix
                        .Substring(0, parsedMediatype.SubTypeWithoutSuffix.Length - 8)
                    : parsedMediatype.SubTypeWithoutSuffix;


                //当请求的媒体类型为简化资源
                if (primaryMediaType == "vnd.crip.Area.simplify")
                {
                    areasSimplifyDto = _mapper
                    .Map<IEnumerable<AreaSimplifyDto>>(areaRepo);
                }
                else//非简化资源时
                {
                    areasDto = _mapper
                   .Map<IEnumerable<AreaDto>>(areaRepo);
                }
            }
            #endregion

            #region 分类封装

            if (isHateoas)//请求Hateoas媒体资源
            {
                var links = CreateLinksForAreaList(areaParameter, paginationResourceParamaters);
                // var links = AreaLinksForAreaId(IDs);
                if (primaryMediaType == "vnd.crip.Area.simplify")//简略资源时
                {
                    areasSimplifyDto.ToList().ForEach(x => { x.Link = CreateLinkForArea(x.Id); });
                    var hateoasRes = new
                    {
                        Areas = areasSimplifyDto,
                        links = links
                    };
                    pagesResponse.Success(hateoasRes, "已查询");
                    return Ok(pagesResponse);
                }
                else                                              //整体资源时
                {
                    areasDto.ToList().ForEach(x => { x.Link = CreateLinkForArea(x.Id); });
                    var hateoasRes = new
                    {
                        Areas = areasDto,
                        links = links
                    };
                    pagesResponse.Success(hateoasRes, "已查询");
                    return Ok(pagesResponse);
                }

            }
            else          //请求非Hateoas媒体资源
            {
                if (primaryMediaType == "vnd.crip.Area.simplify")//简略资源时
                {
                    areasSimplifyDto.ToList().ForEach(x => { x.Link = CreateLinkForArea(x.Id); });
                    var hateoasRes = new
                    {
                        Areas = areasSimplifyDto,
                    };
                    pagesResponse.Success(hateoasRes, "已查询");
                    return Ok(pagesResponse);
                }
                else                                              //整体资源时
                {
                    areasDto.ToList().ForEach(x => { x.Link = CreateLinkForArea(x.Id); });
                    var hateoasRes = new
                    {
                        Areas = areasDto,
                    };
                    pagesResponse.Success(hateoasRes, "已查询");
                    return Ok(pagesResponse);
                }
            }

            #endregion
        }
        #endregion

        #region 创建区域
        /// <summary>
        /// 登陆后创建区域
        /// </summary>
        /// <param name="areaCreationParameter">区域名，地址名，描述</param>
        /// <returns></returns>
        [HttpPost(Name = "CreateArea")]
        [Authorize(AuthenticationSchemes = "Bearer")]
        public async Task<IActionResult> CreateAreaAsync(
          [FromBody]
           AreaCreationParameter areaCreationParameter
           )
        {
            #region 获取当前用户并校验
            PagesResponse pagesResponse = new PagesResponse();
            var userId = _httpContextAccessor
           .HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;//获取jwt中的UserId
            var user = await _userManager.FindByIdAsync(userId);
            if (user == null)
            {
                pagesResponse.NotFound("未找到用户");
                return NotFound(pagesResponse);
            }
            if (user.AreaId != null)
            {
                pagesResponse.BadRequest("您已加入区域，请退出后创建");
                return BadRequest(pagesResponse);
            }
            #endregion

            #region 生成area
            var area = new Area
            {
                Id = Guid.NewGuid(),
                Name = areaCreationParameter.Name,
                Place = areaCreationParameter.Place,
                Description = areaCreationParameter.Description,
                CreatedDate = DateTime.Now.ToString(@"yyyy/MM/dd HH:mm:ss"),
                Members_Number = 1,
                ResponsiblePersonId = userId,
            };
            #endregion

            #region 保存修改
            await _areaRepository.AddAreaAsync(area);
            user.AreaId = area.Id;
            user.Area = area;
            await _areaRepository.SaveAsync();
            #endregion

            #region 封装与返回
            var areaRes = _mapper.Map<AreaDto>(area);
            var Res = new
            {
                Area = areaRes
            };

            pagesResponse.Success(Res, "添加成功");
            return CreatedAtRoute(
                "GetAreaById",
                new { areaId = area.Id },
                pagesResponse
                );
            #endregion

        }
        #endregion

        #region 通过areaId获得Area 
        /// <summary>
        /// 通过areaId获得Area
        /// </summary>
        /// <param name="areaId">areaId</param>
        /// <returns></returns>
        [HttpGet("{areaId:Guid}", Name = "GetAreaById")]
        public async Task<IActionResult> GetAreaByIdAsync(
           [FromRoute] Guid areaId
           )
        {
            PagesResponse pagesResponse = new PagesResponse();

            var area = await _areaRepository.GetAreaByAreaIdAsync(areaId);
            if (area == null)
            {
                pagesResponse.NotFound("区域不存在");
                return NotFound(pagesResponse);
            }
            var areaRes = _mapper.Map<AreaDto>(area);
            var res = new
            {
                Area = areaRes
            };
            pagesResponse.Success(res, "查询成功");
            return Ok(pagesResponse);
        }
        #endregion

        #region 加入区域
        /// <summary>
        /// 登陆后通过areaId加入区域
        /// </summary>
        /// <param name="areaId">区域Id</param>
        /// <returns></returns>
        [Authorize(AuthenticationSchemes = "Bearer")]
        [HttpPost("{areaId:Guid}")]
        public async Task<IActionResult> JionAreaAsync([FromRoute] Guid? areaId)
        {
            PagesResponse pagesResponse = new PagesResponse();
            var useId = _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
            var user = await _userManager.FindByIdAsync(useId);
            if (user == null)
            {
                pagesResponse.NotFound("用户不存在");
                return NotFound(pagesResponse);
            }
            var area = await _areaRepository.GetByIdAsync(areaId);
            if (area == null)
            {
                pagesResponse.NotFound("区域不存在");
                return NotFound(pagesResponse);
            }
            if (areaId == user.AreaId)
            {
                pagesResponse.BadRequest("您已加入该区域,请勿重复加入");
                return BadRequest(pagesResponse);
            }
            else
            {
                var oldArea = await _areaRepository.GetByIdAsync(user.AreaId);
                user.AreaId = area.Id;
                pagesResponse.Success("已加入 " + area.Name);
                area.Members_Number++;
                if (oldArea != null)
                {
                    oldArea.Members_Number--;
                    pagesResponse.Success("已退出 " + oldArea.Name + "， 已加入 ");
                }
            }

            await _areaRepository.SaveAsync();
            return Ok(pagesResponse);
        }
        #endregion

        #region 解散区域 

        /// <summary>
        /// 解散区域
        /// </summary>
        /// <param name="areaId">区域Id</param>
        /// <returns>删除状态</returns>
        [HttpDelete("{areaId:Guid}")]
        [Authorize(AuthenticationSchemes = "Bearer")]
        public async Task<IActionResult> DisbandAreaAsync([FromRoute] Guid areaId)
        {
            PagesResponse pagesResponse = new PagesResponse();
            var area = await _areaRepository.GetByIdAsync(areaId);
            var userId = _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
            if (area.ResponsiblePersonId != userId)
            {
                pagesResponse.BadRequest("您没有该权限");
                return BadRequest(pagesResponse);
            }
            else
            {
                _areaRepository.Delete(area);
                _areaRepository.DeleteUserAreaByAreaId(areaId);
                _eventRepository.DeleteEventsByAreaId(areaId);
                await _areaRepository.SaveAsync();
                pagesResponse.Success("已解散");
                return Ok(pagesResponse);
            }
        }
        #endregion

        #region 退出区域
        /// <summary>
        /// 退出区域
        /// </summary>
        /// <returns></returns>
        [HttpDelete]
        [Authorize(AuthenticationSchemes = "Bearer")]
        public async Task<IActionResult> ExitAreaAsync()
        {
            PagesResponse pagesResponse = new PagesResponse();
            var userId = _httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;
            var user = await _userManager.FindByIdAsync(userId);
            if (user == null)
            {
                pagesResponse.BadRequest("未找到用户");
                return BadRequest(pagesResponse);
            }
            if (user.AreaId == null)
            {
                pagesResponse.BadRequest("未找到所在区域");
                return BadRequest(pagesResponse);
            }
            user.Area = null;
            user.AreaId = null;
            _areaRepository.SaveAsync();
            pagesResponse.Success("已退出");
            return Ok(pagesResponse);

        }
        #endregion

    }
}
